<?php
/**
 * 
 * @author huangyusheng
 * @since 2014-4-16
 * @package project_name.package_name
 */
class Pfinal_Model_QueryClient {
	const OPT_PRE = 'pre';
	const OPT_NEXT = 'next';
	const OPT_SEL = 'response';
	protected $responseStack = array ();
	
	/**
	 * [doQuery description]
	 * 
	 * @param SelectorChain $chain
	 *        	[description]
	 * @param [type] $optimizer
	 *        	[description]
	 * @return [type] [description]
	 */
	public function doQuery(Pfinal_Model_SelectorChain $chain, Pfinal_Model_Optimizer_Interface $optimizer = null) {
		$this->responseStack = array (); // 初始化
		if ($optimizer === null) {
			$optimizer = new Pfinal_Model_Optimizer_Default();
		}
		
		$chainFilter = $chain->getFilterStack ();
		// 首先对查询链进行optimize
		$optimized = $optimizer->optimize ( $chain );
		
		// 分发查询
		foreach ( $optimized as $selector ) {
			$adapter = $selector->getAdapter ();
			$filterChain = $selector->getFilterStack ();
			
			foreach ( $filterChain as $filter ) {
				if ($filter instanceof Pfinal_Model_Filter_BeforeProtocolParse)
					$filter->beforeProtocolParse ( $selector );
			}
			
			$response = $adapter->query ( $selector );
			
			$this->responseStack [] = array (
					Pfinal_Model_SelectorChain::OPT_PRE => $selector->getPre (),
					self::OPT_SEL => $response,
					Pfinal_Model_SelectorChain::OPT_NEXT => $selector->getNext () 
			);
			// 将结果压入堆栈,同时维护查询链的join关系不变
		}
		$dataSet = $this->processJoin ( $optimized );
		
		if (! empty ( $chainFilter )) {
			foreach ( $chainFilter as $filter ) {
				if ($filter instanceof Pfinal_Model_Filter_AfterQuery)
					$filter->afterQuery ( $dataSet, $optimized );
			}
		}
		return $dataSet;
	}
	
	/**
	 * merge最终的dataSet
	 * 
	 * @param SelectorChain $chain
	 *        	[description]
	 * @return [type] [description]
	 */
	protected function processJoin(Pfinal_Model_SelectorChain $chain) {
		foreach ( $this->responseStack as $k => $response ) {
			$tmpK = $k + 1;
			if (! isset ( $this->responseStack [$tmpK] ))
				break;
			$opt = $this->responseStack [$tmpK] [Pfinal_Model_SelectorChain::OPT_PRE];
			switch ($opt) {
				case Pfinal_Model_SelectorChain::OPT_LEFT_JOIN :
					$this->leftJoin ( $this->responseStack [$k] [self::OPT_SEL], $this->responseStack [$tmpK] [self::OPT_SEL] );
					break;
				case Pfinal_Model_SelectorChain::OPT_FULL_JOIN :
					$this->fullJoin ( $this->responseStack [$k] [self::OPT_SEL], $this->responseStack [$tmpK] [self::OPT_SEL] );
					break;
				case Pfinal_Model_SelectorChain::OPT_UNION :
					break; // unimplement
			}
			unset ( $this->responseStack [$tmpK] );
		}
		return $this->responseStack [0] [self::OPT_SEL];
	}
	
	/**
	 * 左连接
	 * 
	 * @param array $subjectDataSet        	
	 * @param array $objectDataSet        	
	 */
	private function leftJoin(&$subjectDataSet, &$objectDataSet) {
		if (empty ( $subjectDataSet ['DATA'] ) || empty ( $objectDataSet ['DATA'] )) {
			return;
		}
		$dataRow = reset ( $objectDataSet ['DATA'] );
		foreach ( $subjectDataSet ['DATA'] as $tmpKey => $tmpRow ) {
			if (isset ( $objectDataSet ['DATA'] [$tmpKey] )) {
				$subjectDataSet ['DATA'] [$tmpKey] = array_merge ( $subjectDataSet ['DATA'] [$tmpKey], $objectDataSet ['DATA'] [$tmpKey] );
			} else {
				foreach ( $dataRow as $tmpField => $tmpVal ) {
					if (! isset ( $subjectDataSet ['DATA'] [$tmpKey] [$tmpField] )) {
						$subjectDataSet ['DATA'] [$tmpKey] [$tmpField] = '--';
					}
				}
			}
		}
		return;
	}
	
	/**
	 *
	 *
	 * 全外连接
	 * 
	 * @param array $subjectDataSet        	
	 * @param array $objectDataSet        	
	 */
	private function fullJoin(&$subjectDataSet, &$objectDataSet) {
		// 当追加数据为空时返回
		if (empty ( $objectDataSet ['DATA'] )) {
			return;
		}
		if (! empty ( $subjectDataSet ['DATA'] )) {
			$dataRow = reset ( $subjectDataSet ['DATA'] );
			foreach ( $objectDataSet ['DATA'] as $tmpKey => $tmpRow ) {
				if (isset ( $subjectDataSet ['DATA'] [$tmpKey] )) {
					$subjectDataSet ['DATA'] [$tmpKey] = array_merge ( $subjectDataSet ['DATA'] [$tmpKey], $tmpRow );
				} else {
					$subjectDataSet ['DATA'] [$tmpKey] = $tmpRow;
					foreach ( $dataRow as $tmpField => $tmpVal ) {
						if (! isset ( $subjectDataSet [$tmpKey] [$tmpField] )) {
							$subjectDataSet ['DATA'] [$tmpKey] [$tmpField] = '--';
						}
					}
				}
				unset ( $tmpRow );
			}
		} else {
			$subjectDataSet = $objectDataSet;
		}
		
		return;
	}
}

?>